using System;
using System.Collections.Generic;
using UnityEngine;

/*
在RPG类游戏开发中，不可避免的会有复杂的数值计算，同一个数值条目可能存在不同对象的同时影响。
比如角色的攻击力，计算时不可避免的会遇到合时做加法，何时做乘法的问题。
武器装备提供的数值倍率增幅也存在是独立乘区还是单一乘区的计算问题。

Value数值类型，提供了完善的RPG类游戏数值计算功能，使用Value数值可以使游戏中不同乘区，不同来源增量的计算变得清晰方便。
Value类型中维护了一个AffectList数组代表多个影响层级，AffectList类维护了一个字典类型对象来存储不同来源的影响（影响指的是对于数值的增量或倍增）
直接使用AddAffect()方法，即可在Value类型的对象里添加一个影响，这个影响可以自定义其计算层级、计算类型。
AddAffect()方法将返回其操作的AffectList对象，可以对该对象进行直接操作以在同一影响层级里增添或修改影响。
AffectList类使用键值对来存储每个影响的来源和数值，这可以保证每个影响不会被重复计算。
使用AffectList对象的Add()方法直接操作影响层。

使用Value类的value属性可以直接获取所有影响层计算完毕之后的总值，你也可以通过GetListAffet()获取每一个影响层的总值。
如果不需要太多的数值叠加，可以直接对value进行赋值操作，相当于对影响层0的“Base”影响（或称基础值）操作。使用Add()直接增减基础值。

除此之外，还定义了RPGValue类型和Demage类型两种结构体。
RPGValue类型里有攻击力，防御力和抗性三种属性。
Demage类有伤害类型列表和伤害值两种属性，定义了添加和查询伤害类型的方法。
*/

/// <summary>
/// 影响类型
/// </summary>
[Serializable]
public enum Affecstype{
    /// <summary>
    /// 增量类型
    /// </summary>
    Plus,
    /// <summary>
    /// 乘区类型
    /// </summary>
    Mul
}

/// <summary>
/// 影响
/// </summary>
[Serializable]
public class Affect{
    /// <summary>
    /// 影响来源
    /// </summary>
    public string source;
    /// <summary>
    /// 影响数值
    /// </summary>
    public float value;
    public Affect(string _source, float _value){
        source = _source;
        value = _value;
    }
}

/// <summary>
/// 影响列表，维护了一个影响列表，用于存储数值影响的同时记录其来源，避免相同来源的影响重复叠加。
/// </summary>
[Serializable]
public class AffectList{
    /// <summary>
    /// 影响列表
    /// </summary>
    public List<Affect> list;
    /// <summary>
    /// 影响类型
    /// </summary>
    public Affecstype type;
    public override string ToString(){
        string str = "";
        lock(list)foreach(Affect item in list){
            str += "[" + item.source + ": " + item.value + "] ";
        }
        return type + "{" + str + "}";
    }

    /// <summary>
    /// 添加影响，已存在相同来源的影响则覆盖其数值。
    /// </summary>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回是否已经存在相同来源的影响</returns>
    public bool Add(string source, float value){
        if(list == null){
            list = new List<Affect>();
        }
        lock(list)if(list.Exists(a => a.source.Equals(source))){
            list.Find(a => a.source.Equals(source)).value = value;
            return true;
        }else{
            list.Add(new Affect(source, value));
            return false;
        }
    }


    /// <summary>
    /// 添加新影响，已存在相同来源的影响则不操作。
    /// </summary>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回是否已经存在相同来源的影响</returns>
    public bool AddNew(string source, float value){
        lock(list)if(list.Exists(a => a.source.Equals(source))){
            return true;
        }else{
            list.Add(new Affect(source, value));
            return false;
        }
    }


    /// <summary>
    /// 添加或修改基础影响
    /// </summary>
    /// <param name="value">基础影响值</param>
    /// <returns>返回是否已存在基础影响</returns>
    public bool Add(float value){
        return Add("Base", value);
    }


    /// <summary>
    /// 移除影响
    /// </summary>
    /// <param name="source">影响来源</param>
    /// <returns>是否存在该来源的影响</returns>
    public bool Remove(string source){
        Affect a;
        lock(list)if((a = list.Find(a => a.source.Equals(source))) != null){
            list.Remove(a);
            return true;
        }else{
            return false;
        }
    }


    /// <summary>
    /// 获取影响值
    /// </summary>
    /// <param name="source">影响来源</param>
    /// <returns>返回影响值，无该来源则返回0</returns>
    public float Get(string source){
        if(list.Exists(a => a.source.Equals(source))){
            return list.Find(a  => a.source.Equals(source)).value;
        }else{
            return 0;
        }
    }


    /// <summary>
    /// 获取Base值
    /// </summary>
    /// <returns></returns>
    public float GetBase(){
        return Get("Base");
    }


    /// <summary>
    /// 获取所有影响相加
    /// </summary>
    /// <returns></returns>
    public float GetAll(){
        float res = 0;
        lock(list)foreach(Affect item in list){
            res += item.value;
        }
        return res;
    }


    /// <summary>
    /// 清空影响
    /// </summary>
    public void Clear(){
        list = new List<Affect>();
    }


    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="type">影响组类型</param>
    /// <param name="b">初始值</param>
    public void init(){
        Clear();
        //乘区类型基础值大于0，如果为0则初始化为1
        if(type == Affecstype.Mul) Add(1);
        else Add(0);
    }


    //构造
    public AffectList(Affecstype type = Affecstype.Plus){
        this.type = type;
        list = new List<Affect>();
        init();
    }
}

/// <summary>
/// RPG数值
/// </summary>
[Serializable]
public struct RPGValue
{   
    public static readonly RPGValue NaN = new RPGValue(DamageType.None);
    /// <summary>
    /// 对应的伤害类型
    /// </summary>
    public DamageType type;
    /// <summary>
    /// 攻击力
    /// </summary>
    public Value attack;
    /// <summary>
    /// 攻击速度
    /// </summary>
    public Value attackspeed;
    /// <summary>
    /// 暴击率
    /// </summary>
    public Value critrate;
    /// <summary>
    /// 暴击伤害
    /// </summary>
    public Value critdamage;
    /// <summary>
    /// 防御力
    /// </summary>
    public Value defen;
    /// <summary>
    /// 抗性
    /// </summary>
    public Value resist;
    public RPGValue(DamageType type){
        this.type = type;
        attack = new Value(0);
        attackspeed = new Value(1);
        critrate = new Value(0);
        critdamage = new Value(1f);
        defen = new Value(0);
        resist = new Value(0);
    }
    //数值合并
    public static RPGValue operator+ (RPGValue a, RPGValue b){
        RPGValue v = new RPGValue(DamageType.None);
        v.attack.value = a.attack.value + b.attack.value;
        v.attackspeed.value = a.attackspeed.value + b.attackspeed.value;
        v.critrate.value = (a.critrate.value+1) * (b.critrate.value+1) - 1;
        v.critdamage.value = a.critdamage.value * b.critdamage.value;
        v.defen.value = a.defen.value + b.defen.value;
        v.resist.value = a.resist.value + b.resist.value;
        return v;
    }
    public float GetDamage(float offset){
        UnityEngine.Random.InitState((int)System.DateTime.Now.Ticks);
        if(UnityEngine.Random.Range(0f, 1f) < critrate.value){
            return (attack.value+offset) * critdamage.value;
        }else{
            return attack.value+offset;
        }
    }
}

/// <summary>
/// 伤害类型
/// </summary>
public enum DamageType{
    //不确定
    None = 0,
    
    //来源类型
    Melee,
    Range,
    Effect,
    
    //主要类型
    Normal,
    True,
    Dot,
    Physical,
    Magic,
    
    //次要类型
    Fire,
    Ice,
    Lighting,
    Poision,
    Darkness,
    Nature,
    Holy,
}
/// <summary>
/// 伤害类
/// </summary>
[Serializable]
public struct Damage{
    /// <summary>
    /// 主类型，如物理伤害，魔法伤害等
    /// </summary>
    [Tooltip("主类型，如物理伤害，魔法伤害")]
    public DamageType type;
    /// <summary>
    /// 元素类型，如火焰伤害，寒冰伤害等
    /// </summary>
    [Tooltip("元素类型，如火焰伤害，寒冰伤害")]
    public DamageType etype;
    /// <summary>
    /// 来源类型，如远程伤害，近战伤害等
    /// </summary>
    [Tooltip("来源类型，如远程伤害，近战伤害")]
    public DamageType stype;
    public float value;

    public Damage(DamageType type = DamageType.True, DamageType etype = DamageType.None, DamageType stype = DamageType.None, float value = 0){
        this.type = type;
        this.etype = etype;
        this.stype = stype;
        this.value = value;
    }
    /// <summary>
    /// 伤害计算
    /// </summary>
    /// <param name="d"></param>
    /// <param name="r"></param>
    /// <returns></returns>
    public static Damage Calculate(Damage d, RPGValue r){
        float dd = d.value - r.defen.value;
        dd = Mathf.Max(dd, 0);
        d.value = dd * (1 -r.resist.value);
        return d;
    }
}

/// <summary>
/// <para>数值类，用于存储RPG游戏会用到的数值，方便进行不同乘区和增量的计算。</para>
/// <para>使用value属性访问</para>
/// </summary>
[Serializable]
public class Value
{   
    public static Value zero = new Value(0);

    [SerializeField]
    [HideInInspector]
    private float _value;
    
    /// <summary>
    /// <para>影响层，分割不同层级和同一层级的影响以达到乘区划分计算的效果。</para>
    /// <para>0层为基础层，存放基础值</para>
    /// </summary>
    [SerializeField]
    private List<AffectList> affects;
    
    public List<AffectList> GetAffects(){
        return affects;
    }
    
    /// <summary>
    /// 获取影响层数量
    /// </summary>
    public int affectCount{
        get{
            return affects.Count;
        }
    }


    /// <summary>
    /// 基础数值影响层
    /// </summary>
    [HideInInspector]
    public AffectList baseValue;
    /// <summary>
    /// 基础乘区影响层
    /// </summary>
    [HideInInspector]
    public AffectList mulValue;
    /// <summary>
    /// 增加值影响层
    /// </summary>
    public AffectList increValue;

    # region 影响层操作

    /// <summary>
    /// 在指定影响层内添加影响，必须满足影响层的类型,同步
    /// </summary>
    /// <param name="index">影响层</param>
    /// <param name="type">影响类型</param>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回指定的影响层对象</returns>
    public AffectList AddAffect(int index, Affecstype type, string source, float value){
        AffectList res;
        lock(affects){
            if(affects.Count <= index){
                //如果增加的影响层比当前影响层高，则增加影响层。
                for(int i = affects.Count; i<=index; i++){
                    affects.Add(new AffectList());
                }
                //当前层的类型赋值为添加影响的类型。
                affects[index].type = type;
                affects[index].init();
            }
            if(affects[index].type == type){
                //如果影响层类型相同，添加影响
                affects[index].Add(source, value);
                res = affects[index];
            }else{
                //如果影响层类型不同，返回null
                res = null;
            }
        }
        //在Add里更新数值
        Update();
        return res;
    }


    /// <summary>
    /// 在指定层内添加影响，无视影响层的类型。
    /// </summary>
    /// <param name="index">影响层</param>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回指定的影响层对象</returns>
    public AffectList AddAffect(int index, string source, float value){
        AffectList _a = AddAffect(index, Affecstype.Plus, source, value);
        if(_a == null){
            _a = AddAffect(index, Affecstype.Mul, source, value);
        }
        return _a;
    }


    /// <summary>
    /// 添加最新的影响层并添加影响
    /// </summary>
    /// <param name="index">影响层</param>
    /// <param name="type">影响类型</param>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回指定的影响层对象</returns>
    public AffectList AddAffect(Affecstype type, string source, float value){
        return AddAffect(affects.Count, type, source, value);
    }


    /// <summary>
    /// 设置基础层的基础值
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public AffectList Set(float value){
        return AddAffect(0, Affecstype.Plus, "Base", value);
    }


    /// <summary>
    /// 获取影响层
    /// </summary>
    /// <param name="index">影响层</param>
    /// <returns>返回影响层的AffectList对象</returns>
    public AffectList GetAffect(int index){
        if(index >= affects.Count){
            AddAffect(index, "Base", 0);
        }
        Update();
        return affects[index];
    }


    /// <summary>
    /// 获取基础层的基础值
    /// </summary>
    /// <returns></returns>
    public float Get(){
        return GetAffect(0).Get("Base");
    }


    /// <summary>
    /// 获取指定影响层的结果
    /// </summary>
    /// <param name="index">影响层</param>
    /// <returns></returns>
    public float GetListAffect(int index){
        return GetAffect(index).GetAll();
    }


    /// <summary>
    /// 获取所有影响层的结果
    /// </summary>
    /// <returns></returns>
    public float GetListAffectAll(){
        float res = 0;
        lock(affects)foreach(AffectList item in affects){
            if(item.type == Affecstype.Mul){
                res *= item.GetAll();
            }else if(item.type == Affecstype.Plus){
                res += item.GetAll();
            }
        }
        return res;
    }

    public bool RemoveAffect(int id, string source){
        bool b = GetAffect(id).Remove(source);
        Update();
        return b;
    }

    /// <summary>
    /// 设置影响层的类型，并初始化影响层。
    /// </summary>
    /// <param name="index">影响层</param>
    /// <param name="type">影响层类型</param>
    /// <returns>返回修改之前的影响层类型</returns>
    public Affecstype Sestype(int index, Affecstype type){
        Affecstype _t = GetAffect(index).type;
        GetAffect(index).type = type;
        GetAffect(index).init();
        return _t;
    }



    /// <summary>
    /// 强制添加影响，添加前会初始化目标层并设置为目标类型。
    /// </summary>
    /// <param name="index">影响层</param>
    /// <param name="type">影响类型</param>
    /// <param name="source">影响来源</param>
    /// <param name="value">影响值</param>
    /// <returns>返回指定的影响层对象</returns>
    public AffectList AddAffectForce(int index, Affecstype type, string source, float value){
        Sestype(index, type);
        return AddAffect(index, type, source, value);
    }



    /// <summary>
    /// 直接增减基础值（不建议使用）
    /// </summary>
    /// <param name="value"></param>
    public void Add(float value){
        Set(Get() + value);
    }
    
    public void Update(){
        _value = GetListAffectAll();
    }

    #endregion

    /// <summary>
    /// 直接量
    /// <para>get: 获取所有影响层的结果</para>
    /// <para>set：直接修改基础层的基础值</para>
    /// </summary>
    public float value{
        get{
            return _value;
        }
        set{
            Set(value);
        }
    }

    //运算符重载
    
    public static Value operator+ (Value a, float b){
        a.Add(b);
        return a;
    }

    public static Value operator* (Value a, float b){
        a.AddAffect(1, Affecstype.Mul, "Base", b);
        return a;
    }
    
    public static implicit operator float(Value a){
        return a.value;
    }
    
    public static implicit operator Value(float a){
        return new Value(a);
    }

    //构造
    public Value(float _base = 0){
        affects = new List<AffectList>();
        baseValue = AddAffect(0,Affecstype.Plus,"Base",0);
        mulValue = AddAffect(1,Affecstype.Mul,"Base",1);
        increValue = AddAffect(2,Affecstype.Plus,"Base",0);
        value = _base;
        Update();
    }

    public override string ToString(){
        return value.ToString();
    }

    public override bool Equals(object obj){
        Value other = (Value)obj;
        return value == other.value;
    }

    public override int GetHashCode(){
        return base.GetHashCode();
    }
}
